package com.GreenIce.android.util;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;

public class DirectionIdentifier implements SensorEventListener{
	//public static int[] SENSOR_TYPES=new int[]{Sensor.TYPE_ACCELEROMETER};
	public static final int ANGLE_RANGE = 40;
	public static final float INVALID = 1e10f;
	public static DirectionIdentifier dir;
	
    private Sensor magSensor;
    private Sensor accSensor;
	/**
	 * 获取实例
	 * @return
	 */
	public static DirectionIdentifier getInstance(){
		if (dir == null){
			dir = new DirectionIdentifier();
		}
		return dir;
	}
	/**
	 * 记录的此时的方向为正前方
	 */
	public static void setRelativeDirection(){
		dir.setRelative();
	}
	private void setRelative() {
		updateCoords();
		rel_up = getPhoneLongSidePointing();
		rel_right = getPhoneShortSidePointing();
		rel_front = getAngleOfView();
	}
	/**
	 * 返回当前的方向, 0表示非法方向,1-6表示6个方向. 1前 2后  3左 4右
	 * @return
	 */
	public static int getCurrentDirection(){
		return dir.getDir();
	}
	//private int sensor_type = 0;
	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		//DO nothing...
	}
	int cur_dir = 0;
	long time_stamp;
	//public float rx = 0,ry = 0,rz = 0,x,y,z;
	Vector3 accData;
	Vector3 magData;
	Vector3 up,north,east;
	Vector3 rel_up = new Vector3(0,0,1),rel_front = new Vector3(0,1,0),rel_right = new Vector3(1,0,0);
	@Override
	public void onSensorChanged(SensorEvent event) {
		Vector3 data = new Vector3(event.values);
		//Log.i("DirSensor", "onSensorChanged:"+data+"TYPE:"+event.sensor.getName());
		switch (event.sensor.getType()){
		case Sensor.TYPE_ACCELEROMETER :{
			accData = data;
			break;
		}
		case Sensor.TYPE_MAGNETIC_FIELD :{
			magData = data;
			break;
		}
		default : Log.w("onSensorChanged", "Unknown Sensor Type:"+event.sensor.getName());
		}
	}
	public Vector3 getAngleOfView(){
		//calculate "down(0,0,-1)" z in <east,north,up> 
		Vector3 viewAngle = new Vector3(0,0,-1);
		viewAngle.rotate(east, north, up);				
		return viewAngle;
	}
	public Vector3 getPhoneLongSidePointing(){
		//calculate "phone_y(0,1,0)" z in <east,north,up> 
		Vector3 phone_y = new Vector3(0,1,0);
		phone_y.rotate(east, north, up);				
		return phone_y;		
	}
	public Vector3 getPhoneShortSidePointing(){
		//calculate "phone_y(1,0,0)" z in <east,north,up> 
		Vector3 phone_x = new Vector3(1,0,0);
		phone_x.rotate(east, north, up);				
		return phone_x;		
	}
	private void updateCoords(){
		//up towards the sky;
		up = accData.copy();
		up.normolize();
		//north along the ground
		north = magData.copy();
		north.normolize();
		north.add(up.copy().scale(-north.scalorProduct(up)));
		north.normolize();
		//east along the ground
		east = north.vectorProduct(up);
		east.normolize();
	}
	//返回当前的方向, 0表示非法方向,1-6表示6个方向. 1前 2后  3左 4右  5上 6下
	private int getDir() {	
		updateCoords();
		Vector3 aov = getAngleOfView();
		double angle = aov.getAngle(rel_front);
		if (angle < ANGLE_RANGE)
			return 1;
		else if (angle > 180 - ANGLE_RANGE)
			return 2;
		angle = aov.getAngle(rel_right);
		if (angle < ANGLE_RANGE)
			return 4;
		else if (angle > 180 - ANGLE_RANGE)
			return 3;
		angle = aov.getAngle(rel_up);
		if (angle < ANGLE_RANGE)
			return 5;
		else if (angle > 180 - ANGLE_RANGE)
			return 6;
		
 		return 0;
	}
	
	public void register(SensorManager mSensorManager) {
		if (accSensor == null)
			accSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(this, accSensor, SensorManager.SENSOR_DELAY_NORMAL);
        
        if (magSensor == null)
        	magSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        mSensorManager.registerListener(this, magSensor, SensorManager.SENSOR_DELAY_NORMAL);
	}
	public void unregister(SensorManager mSensorManager) {
    	mSensorManager.unregisterListener(this);
	}
	class Vector3 {
		float x,y,z;
		float[] data;
		public Vector3(float[] f){
			if (f.length != 3){
				Log.e("Vector3", "length is not 3!");			
			} else {
				data = f.clone();
				x = data[0];
				y = data[1];
				z = data[2];
			}		
		}
		public double getAngle(Vector3 v) {
			double cos = scalorProduct(v)/( getLength() * v.getLength()); 			
			return Math.acos(cos)/Math.PI * 180;
		}
		public Vector3(float x, float y, float z){
			this.x = x;
			this.y = y;
			this.z = z;		
			data = new float[] {x,y,z};		
		}
		@Override
		public String toString() {
			
			return String.format("\n%.2f\n%.2f\n%.2f",x,y,z);
		}
		
		public Vector3 normolize(){
			double len = getLength();
			x /= len;
			y /= len;
			z /= len;	
			return this;
		}
		public double getLength(){
			return Math.sqrt(x*x+y*y+z*z);
		}
		public Vector3 copy(){
			return new Vector3(x,y,z);	
		}
		public float scalorProduct(Vector3 v){
			return x*v.x + y*v.y + z*v.z;
		}
		public Vector3 scale(float f){
			x *= f;
			y *= f;
			z *= f;
			return this;
		}
		public void add(Vector3 v){
			x += v.x;
			y += v.y;
			z += v.z;
		}
		public Vector3 vectorProduct(Vector3 v) {
			return new Vector3(
					y*v.z - z*v.y,
					z*v.x - x*v.z,
					x*v.y - y*v.x
					);		
		}
		public Vector3 rotate(Vector3 vx, Vector3 vy, Vector3 vz){		
			float xx = scalorProduct(vx);
			float yy = scalorProduct(vy);
			float zz = scalorProduct(vz);
			x = xx;
			y = yy;
			z = zz;
			return this;
		}
		
		public float sum(){
			return x + y + z;
		}
	}

}
